package com.winit.commons.task;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.*;

/**
 * Created by wangjian on 16/8/9.
 */
public class ChainBaseTask extends Task {

    private static final Logger logger = LoggerFactory.getLogger(ChainBaseTask.class);

    /**
     * 任务列表
     * 单独执行发布步骤时,taskNodeList长度为1,另需要传入该步骤序号,从0开始
     */
    private List<TaskNode> taskNodeList;
    //任务列表中,每个任务执行的返回状态列表
    private String[] status = null;
    private List<String> taskNodeStatus = new ArrayList<>();

    public List<TaskHandler> handlers = new ArrayList<>();
    //是否暂停中
    private boolean isPause = Boolean.FALSE;

    public ChainBaseTask(String taskId, Integer expireTime, List<TaskNode> taskNodeList) {
        super(taskId, expireTime);
        this.taskNodeList = taskNodeList;
        this.status = new String[taskNodeList.size()];
    }

    public ChainBaseTask(String taskId, Integer expireTime, List<TaskNode> taskNodeList, TaskHandler taskHandler) {
        super(taskId, expireTime);
        this.taskNodeList = taskNodeList;
        this.handlers.add(taskHandler);
    }

    public void addListener(TaskHandler taskHandler) {
        this.handlers.add(taskHandler);
    }

    @Override
    public TaskStatus call() throws Exception {
        logger.info("ChainBaseTask:执行线程开始.taskId=" + this.taskId);

        if (!this.canTaskRun()) {
            logger.error("ChainBaseTask:执行线程,任务不可执行.taskId=" + this.taskId);
            this.taskStatus = TaskStatus.STATUS_ERROR;
            this.message = "ERROR:任务不可执行.";
            this.publishChainTaskStatusAndMessage();
            this.taskClose();
            return this.taskStatus;
        }

        this.taskStatus = TaskStatus.STATUS_RUNNING;
        this.message = "任务执行中";
        this.publishChainTaskStatusAndMessage();

        try {
            this.validate();
        } catch (Exception e) {
            logger.error("ChainBaseTask:执行线程,参数校验失败.taskId=" + this.taskId);
            this.taskStatus = TaskStatus.STATUS_PARAM_ERROR;
            this.message = "ERROR:参数校验失败." + e.getMessage();
            this.publishChainTaskStatusAndMessage();
            this.taskClose();
            return this.taskStatus;
        }
        ExecutorService executor = Executors.newCachedThreadPool();
        try {
            this.run(executor);
            if (!this.isTaskStop) {
                this.taskStatus = TaskStatus.STATUS_SUCCESS;
                this.message = "任务执行完成";
            }
        } catch (Exception e) {
            logger.error("ChainBaseTask:执行任务异常,e:" + e + ",taskId=" + this.taskId);
            this.taskStatus = TaskStatus.STATUS_EXCEPTION;
            this.message = "ERROR:任务执行异常." + e.getMessage();
            throw e;
        } finally {
            executor.shutdownNow();
            this.publishChainTaskStatusAndMessage();
            this.taskClose();
            return this.taskStatus;
        }
    }

    /**
     * 校验本次chaintask是否可执行
     * @return
     * @throws Exception
     */
    private boolean validate() throws Exception {
        logger.info("ChainBaseTask:执行线程,校验参数,taskId=" + this.taskId);
        Integer size = this.taskNodeList.size();
        if (size == 0) {
            logger.error("ChainBaseTask:执行线程,线程列表为空.size=" + size + ",taskId=" + this.taskId);
            throw new Exception("执行线程,参数检验失败,线程列表为空.");
        }
        return true;
    }

    private void run(ExecutorService executor) throws Exception {
        for (int i = 0; i < this.taskNodeList.size(); i++) {
            TaskNode taskNode = this.taskNodeList.get(i);
            Integer index = taskNode.index == -1 ? i : taskNode.index;
            while (this.isPause) {
                Thread.sleep(2000);
                if (!this.isPause || this.isTaskStop) {
                    if (!this.isPause) {
                        this.taskStatus = TaskStatus.STATUS_RUNNING;
                        this.message = "任务执行中";
                        this.publishChainTaskStatusAndMessage();
                        taskNode.task.isContinue = true;
                    }
                    break;
                }
            }
            if (!this.canTaskRun()) {
                logger.error("ChainBaseTask:执行线程,任务不可执行.taskId=" + this.taskId);
                this.taskStatus = TaskStatus.STATUS_ERROR;
                this.message = "ERROR:任务不可执行.";
                this.publishChainTaskStatusAndMessage();
                this.setAndPublishTaskNodeStatus(TaskStatus.STATUS_STOP, index);
                break;
            }
            if (this.isTaskStop) {
                logger.info("ChainBaseTask:任务被停止,退出.taskId=" + this.taskId);
                this.taskStatus = TaskStatus.STATUS_STOP;
                this.message = "任务被停止";
                this.setAndPublishTaskNodeStatus(TaskStatus.STATUS_STOP, index);
                break;
            }

            this.setAndPublishTaskNodeStatus(TaskStatus.STATUS_RUNNING, index);
            Task task = taskNode.task;
            if (task == null) {
                logger.error("ChainBaseTask:taskNode内任务为null");
                throw new Exception("taskNode内任务为null");
            }

            Future<TaskStatus> future = executor.submit(task);

            try {
                TaskStatus s = future.get(taskNode.task.expireTime, TimeUnit.SECONDS);
                /**
                 * 执行单步发布时,需要将状态放到list内指定的位置
                 * 一键发布时,按顺序保存
                 */

                if (s != TaskStatus.STATUS_SUCCESS) {
                    if (taskNode.needFailConfirm) {
                        this.pauseT();
                        this.taskStatus = TaskStatus.STATUS_PAUSE;
                        this.message = "任务暂停";
                        s = TaskStatus.STATUS_PAUSE;
                        this.publishChainTaskStatusAndMessage();
                        i--;
                    } else {
                        this.taskStatus = s;
                        this.message = "ERROR:任务执行失败";
                        i--;
                        this.stopTask();
                    }
                }
                this.setAndPublishTaskNodeStatus(s, index);
            } catch (TimeoutException e) {
                logger.error("ChainBaseTask:执行任务超时.taskId=" + taskNode.task.taskId + ",expireTime=" + taskNode.task.expireTime + ",e:" + e);
                this.taskStatus = TaskStatus.STATUS_TIMEOUT;
                this.message = "ERROR:任务执行超时,请联系系统管理员!";
                this.setAndPublishTaskNodeStatus(this.taskStatus, index);
                i--;
                this.stopTask();
            } catch (Exception e) {
                logger.error("ChainBaseTask:执行任务异常.taskId=" + taskNode.task.taskId + ",e:" + e);
                if (taskNode.needFailConfirm) {
                    this.pauseT();
                    this.taskStatus = TaskStatus.STATUS_PAUSE;
                    this.message = "任务执行异常暂停," + e.getMessage();
                    this.setAndPublishTaskNodeStatus(TaskStatus.STATUS_PAUSE, index);
                    this.publishChainTaskStatusAndMessage();
                    i--;
                } else {
                    this.taskStatus = TaskStatus.STATUS_EXCEPTION;
                    this.message = "ERROR:任务执行异常." + e.getMessage();
                    this.setAndPublishTaskNodeStatus(this.taskStatus, index);
                    this.publishChainTaskStatusAndMessage();
                    i--;
                    this.stopTask();
                }
            }
        }
    }

    private void setAndPublishTaskNodeStatus(TaskStatus taskStatus, Integer index) {
        this.status[index] = taskStatus.getValue();

        if (this.handlers.size() == 0) {
            return;
        }
        this.taskNodeStatus.clear();
        Collections.addAll(this.taskNodeStatus, this.status);
        for (TaskHandler handler : this.handlers) {
            handler.handleTaskNodeStatus(this.taskId, this.taskNodeStatus);
        }
    }

    private void publishChainTaskStatusAndMessage() {
        if (this.handlers.size() == 0) {
            return;
        }
        for (TaskHandler handler : this.handlers) {
            handler.handleChainTaskStatus(this.taskId, this.taskStatus.getValue());
            handler.handleChainTaskMessage(this.taskId, this.message);
        }
    }

    private void publishChainTaskMessage() {
        if (this.handlers.size() == 0) {
            return;
        }
        for (TaskHandler handler : this.handlers) {
            handler.handleChainTaskMessage(this.taskId, this.message);
        }
    }

    /**
     * 任务完成后调用 删除缓存对象
     */
    public void taskClose() {
        logger.info("Task:任务执行完成,删除缓存对象.taskId=" + this.taskId);
        this.taskContainer.removeTask(this.taskId);
    }

    public interface TaskHandler {

        void handleTaskNodeStatus(String taskId, List<String> taskNodeStatus);

        void handleChainTaskStatus(String taskId, String taskStatus);

        void handleChainTaskMessage(String taskId, String message);
    }

    /**
     * 暂停
     */
    private void pauseT() {
        logger.info("ChainBaseTask:当前线程休眠,taskId=" + this.taskId);
        this.isPause = Boolean.TRUE;
    }

    /**
     * 继续执行当前线程
     */
    public void continueTask() {
        logger.info("ChainBaseTask:当前线程结束休眠,继续执行,taskId=" + this.taskId);
        this.isPause = Boolean.FALSE;
        this.taskStatus = TaskStatus.STATUS_CONTINUE;
        this.message = "任务执行中";
        this.publishChainTaskStatusAndMessage();
    }

    //线程开始时，判断是否可以运行 避免发版误操作
    private boolean canTaskRun() {
        return true;
    }

}
